﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.XPath;
using System.IO;
using System.Xml;
using System.Globalization;

namespace MattDotson.GlobalToolkit.ExchangeRateProviders
{
    /// <summary>
    /// 
    /// </summary>
    public class NewYorkFederalReserveProvider : ICurrencyExchangeRateProvider
    {
        IEnumerable<ExchangeRate> exchangeRates;

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        private void Initialize()
        {
            NewYorkFed.FXWSClient client = null;
            try
            {
                client = new NewYorkFed.FXWSClient();
                client.Open();

                string xml = client.getAllLatestNoonRates();
                exchangeRates = ParseSdmx(xml);
            }
            finally
            {
                if (null != client)
                {
                    client.Close();
                }
            }
        }

        /// <summary>
        /// Parses the SDMX returned from FRBNY's web service.
        /// </summary>
        /// <param name="xml">The XML.</param>
        /// <returns></returns>
        private static IEnumerable<ExchangeRate> ParseSdmx(string xml)
        {
            XPathDocument xpd = new XPathDocument(new StringReader(xml));
            XPathNavigator xpn = xpd.CreateNavigator();
            XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xpn.NameTable);
            namespaceManager.AddNamespace("frbny", xmlnsFrbny);
            namespaceManager.AddNamespace("message", xmlnsMessage);
            var seriesNodes = xpn.Select("/message:UtilityData/frbny:DataSet/frbny:Series", namespaceManager);
            foreach (XPathNavigator seriesNode in seriesNodes)
            {
                string from = seriesNode.GetAttribute("UNIT", String.Empty);
                string to = seriesNode.SelectSingleNode("frbny:Key/frbny:CURR", namespaceManager).Value;
                int hour = seriesNode.SelectSingleNode("frbny:Key/frbny:FX_TIME", namespaceManager).ValueAsInt;
                var observationNodes = seriesNode.Select("frbny:Obs", namespaceManager);
                foreach (XPathNavigator observation in observationNodes)
                {
                    DateTime date = observation.SelectSingleNode("frbny:TIME_PERIOD", namespaceManager).ValueAsDateTime;
                    date = new DateTime(date.Year, date.Month, date.Day, hour, 0, 0, DateTimeKind.Unspecified);
                    double rate = observation.SelectSingleNode("frbny:OBS_VALUE", namespaceManager).ValueAsDouble;

                    var easternTimezone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
                    DateTimeOffset asOf = new DateTimeOffset(date, easternTimezone.GetUtcOffset(date));

                    yield return new ExchangeRate { AsOf = asOf, From = from, To = to, Rate = rate };
                }
            }
        }

        private const string xmlnsFrbny = "http://www.newyorkfed.org/xml/schemas/FX/utility";
        private const string xmlnsMessage = "http://www.SDMX.org/resources/SDMXML/schemas/v1_0/message";

        #region ICurrencyExchangeRateProvider Members

        public ExchangeRate GetExchangeRateAsOf(string from, string to, DateTimeOffset asOf)
        {
            Initialize();
            throw new NotImplementedException();
        }

        /// <summary>
        /// Gets the latest exchange rate.
        /// </summary>
        /// <param name="from">From.</param>
        /// <param name="to">To.</param>
        /// <returns></returns>
        public ExchangeRate GetLatestExchangeRate(string from, string to)
        {
            Initialize();

            ExchangeRate retVal = exchangeRates.SingleOrDefault(x => x.From == from && x.To == to);
            return retVal;
        }

        /// <summary>
        /// Gets all exchange rates.
        /// </summary>
        /// <param name="from">From.</param>
        /// <param name="to">To.</param>
        /// <returns></returns>
        public IEnumerable<ExchangeRate> GetAllExchangeRates(string from, string to)
        {
            Initialize();
            throw new NotImplementedException();
        }


        /// <summary>
        /// Gets a value indicating whether this provider supports historical rates.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this provider supports historical rates; otherwise, <c>false</c>.
        /// </value>
        public bool SupportsHistoricalRates
        {
            get { return false; }
        }

        #endregion
    }
}
